package com.atguigu.srb.base.utils;

import com.atguigu.srb.common.exception.BusinessException;
import com.atguigu.srb.common.result.ResponseEnum;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;

/**
 * JWT工具类 - 用于生成、解析和验证JSON Web Token
 * 提供用户认证和授权相关功能
 */
public final class JwtUtils {

    // 私有构造函数，防止实例化
    private JwtUtils() {
    }

    // 令牌有效期（24小时）
    private static final long TOKEN_EXPIRATION = 24 * 60 * 60 * 1000;

    // JWT签名密钥（生产环境建议存储在配置文件或环境变量中）
    private static final String TOKEN_SIGN_KEY = "A1t2g3uigu123456";

    // JWT签名算法
    private static final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;

    // HTTP请求头中Authorization字段的前缀
    private static final String TOKEN_PREFIX = "Bearer ";

    // JWT主题，标识该令牌的用途
    private static final String TOKEN_SUBJECT = "SRB-USER";

    // 预生成签名密钥，提高性能
    private static final Key SIGNING_KEY = generateSigningKey();

    /**
     * 生成HMAC-SHA512签名密钥
     *
     * @return 签名密钥对象
     */
    private static Key generateSigningKey() {
        byte[] keyBytes = DatatypeConverter.parseBase64Binary(TOKEN_SIGN_KEY);
        return new SecretKeySpec(keyBytes, SIGNATURE_ALGORITHM.getJcaName());
    }

    /**
     * 创建JWT令牌
     *
     * @param userId   用户ID
     * @param userName 用户名
     * @return JWT字符串
     */
    public static String createToken(Long userId, String userName) {
        return Jwts.builder().setSubject(TOKEN_SUBJECT)                  // 设置主题
                .setIssuedAt(new Date())                   // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis() + TOKEN_EXPIRATION)) // 设置过期时间
                .claim("userId", userId)                   // 添加用户ID声明
                .claim("userName", userName)               // 添加用户名声明
                .signWith(SIGNATURE_ALGORITHM, SIGNING_KEY) // 设置签名算法和密钥
                .compressWith(CompressionCodecs.GZIP)      // 启用GZIP压缩
                .compact();                                // 生成JWT字符串
    }

    /**
     * 创建完整的授权令牌（包含Bearer前缀）
     *
     * @param userId   用户ID
     * @param userName 用户名
     * @return 完整授权令牌字符串
     */
    public static String createFullToken(Long userId, String userName) {
        return TOKEN_PREFIX + createToken(userId, userName);
    }

    /**
     * 从HTTP Authorization头中提取JWT令牌
     *
     * @param authHeader 授权头，格式为"Bearer token"
     * @return 提取的原始JWT令牌，若格式不匹配则返回null
     */
    public static String extractToken(String authHeader) {
        if (StringUtils.hasText(authHeader) && authHeader.startsWith(TOKEN_PREFIX)) {
            return authHeader.substring(TOKEN_PREFIX.length());
        }
        return null;
    }

    /**
     * 验证JWT令牌有效性
     *
     * @param token JWT令牌字符串
     * @return 有效返回true，无效返回false
     */
    public static boolean validateToken(String token) {
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        try {
            // 解析并验证签名和过期时间
            Jwts.parser().setSigningKey(SIGNING_KEY).parseClaimsJws(token);
            return true;
        } catch (ExpiredJwtException e) {
            // 令牌已过期
            return false;
        } catch (SignatureException | MalformedJwtException | IllegalArgumentException e) {
            // 签名无效、格式错误等
            return false;
        }
    }

    /**
     * 从JWT令牌中获取用户ID
     *
     * @param token JWT令牌字符串
     * @return 用户ID
     * @throws BusinessException 令牌无效或已过期时抛出
     */
    public static Long getUserId(String token) {
        Claims claims = parseTokenClaims(token);
        return Long.valueOf(claims.get("userId").toString());
    }

    /**
     * 从JWT令牌中获取用户名
     *
     * @param token JWT令牌字符串
     * @return 用户名
     * @throws BusinessException 令牌无效或已过期时抛出
     */
    public static String getUserName(String token) {
        Claims claims = parseTokenClaims(token);
        return claims.get("userName", String.class);
    }

    /**
     * 解析JWT令牌获取Claims
     *
     * @param token JWT令牌字符串
     * @return 令牌中的Claims对象
     * @throws BusinessException 令牌无效或已过期时抛出
     */
    private static Claims parseTokenClaims(String token) {
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(ResponseEnum.LOGIN_AUTH_ERROR);
        }
        try {
            return Jwts.parser().setSigningKey(SIGNING_KEY).parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            // 令牌已过期
            throw new BusinessException("令牌已过期");
        } catch (SignatureException | MalformedJwtException | IllegalArgumentException e) {
            // 签名无效、格式错误等
            throw new BusinessException("无效的令牌");
        }
    }

    /**
     * 检查JWT令牌是否已过期
     *
     * @param token JWT令牌字符串
     * @return 已过期返回true，未过期返回false
     */
    public static boolean isTokenExpired(String token) {
        try {
            Claims claims = Jwts.parser().setSigningKey(SIGNING_KEY).parseClaimsJws(token).getBody();
            return claims.getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }

    /**
     * 获取JWT令牌剩余有效时间
     *
     * @param token JWT令牌字符串
     * @return 剩余有效时间（毫秒）
     */
    public static long getRemainingTime(String token) {
        Claims claims = parseTokenClaims(token);
        return claims.getExpiration().getTime() - System.currentTimeMillis();
    }
}